package com.ds.infra.ibatis.binding;

import com.ds.infra.ibatis.session.SqlSession;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 作用： MapperProxy类实现了InvocationHandler接口，用于在动态代理中调用实际的mapper方法。
 *
 * @author WeiShaoying
 */
public class MapperProxy<T> implements InvocationHandler {

    // 我们需要用它来执行实际的SQL操作
    private final SqlSession sqlSession;

    // mapperInterface是这个代理类将要实现的接口类型，比如 UserMapper.class
    private final Class<T> mapperInterface;

    // methodCache是一个缓存，用于存储已经解析的mapper方法信息，这样可以避免每次调用都进行重复解析。
    // key是mapper方法，value是MapperMethod对象
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 如果调用的方法不是接口中的方法，则直接调用代理对象的方法，例如 toString()，equals()等
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        // 获取MapperMethod对象，并执行SQL命令，返回结果
        final MapperMethod mapperMethod = getCachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }

    private MapperMethod getCachedMapperMethod(Method method) {
        // 从缓存中获取MapperMethod对象，如果没有则创建并缓存
        return methodCache.computeIfAbsent(method, key -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
    }
}
